// Copyright 2012 Jay Young. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview
 *
 * A node visitor that finds and documents @typedefs
 */

var Token = com.google.javascript.rhino.Token,
    logger = require('ringo/logging').getLogger('TypedefV');



/**
 * A node visitor that finds and documents @typedefs.
 *
 * @param {boolean=} debug Turns off error checking.  Useful for exploring edge
 *    cases.
 *
 * @constructor
 * @implements {Documenter.Visitor}
 */
var TypedefV = exports.TypedefV = function TypedefV(debug) {
  /**
   * The next visitor in the chain.
   *
   * @type {Documenter.Visitor}
   * @private
   */
  this.nextVisitor_ = null;

  /**
   * @type {boolean}
   * @private
   */
  this.DEBUG_ = !!debug;
};


/**
 * @param {Documenter.Visitor} visitor The next visitor in the chain.
 */
TypedefV.prototype.setNext = function (visitor) {
  this.nextVisitor_ = visitor;
};


/**
 * Determine if a node represents an assignment to a prototype property and
 * is a function.  If the right-hand value is not a function, this will
 * also check whether it is goog.nullFunction or goog.abstractMethod and will
 * document the assignment as a method if so.
 *
 * @param {Node} node The node to visit.
 * @parma {DocInfo} docInfo The page's documentation info object.
 * @param {JSDocInfo} jsdoc The jsdoc for the given node or null if no
 *    doc comments were attached.
 */
TypedefV.prototype.visit = function (node, docInfo, jsdoc) {
  if (jsdoc && jsdoc.hasTypedefType()) {
    var name;
    if (node.isGetProp()) {
      // Handle qualified names
      name = node.getQualifiedName();
    }
    else if (node.isVar()) {
      // Handle "var TypeName;" declarations
      name = node.getFirstChild().getQualifiedName();
    }
    else {
      logger.warning('Unrecognized typedef definition for node:');
      logger.warning(node.toString());
    }

    var type = docInfo.resolveType(jsdoc.getTypedefType()),
        typedefDesc = docInfo.getTypedef(name);

    typedefDesc.name = docInfo.expandAlias(name);
    typedefDesc.type = type;
    typedefDesc.typedefDoc = jsdoc.getBlockDescription() || '';

    if (jsdoc.isDeprecated()) {
      typedefDesc.deprecationReason = jsdoc.getDeprecationReason() || '';
    }

    var refs = jsdoc.getReferences(); // @see links.
    if (refs && refs.size() > 0) {
      var iter = refs.iterator(),
          refList = typedefDesc.references = [];

      while (iter.hasNext()) {
        refList.push(iter.next());
      }
    }
  }

  if (this.nextVisitor_) {
    this.nextVisitor_.visit(node, docInfo, jsdoc);
  }
};
